package com.atguigu.schedule.service;

import com.atguigu.schedule.mapper.SlamSessionMapper;
import com.atguigu.schedule.pojo.SlamSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * SLAM进程管理服务
 * 负责启动、停止和监控SLAM处理进程
 */
@Slf4j
@Service
public class SlamProcessService {
    
    @Autowired
    private SlamSessionMapper slamSessionMapper;
    
    // 存储正在运行的SLAM进程
    private final Map<Long, Process> runningProcesses = new ConcurrentHashMap<>();
    
    // 存储SLAM进程的输出数据
    private final Map<Long, SlamProcessData> processData = new ConcurrentHashMap<>();
    
    // 定时任务执行器
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
    
    /**
     * SLAM进程数据结构
     */
    public static class SlamProcessData {
        public int currentFrame = 0;
        public int totalFrames = 0;
        public int keyframes = 0;
        public int mapPoints = 0;
        public double trajectoryLength = 0.0;
        public double progress = 0.0;
        public String status = "STARTING";
        public LocalDateTime lastUpdate = LocalDateTime.now();
    }
    
    /**
     * 启动SLAM处理进程
     */
    public boolean startSlamProcess(Long sessionId, String videoSource) {
        try {
            SlamSession session = slamSessionMapper.selectById(sessionId);
            if (session == null) {
                throw new RuntimeException("SLAM会话不存在: " + sessionId);
            }
            
            // 检查进程是否已经在运行
            if (runningProcesses.containsKey(sessionId)) {
                log.warn("SLAM进程已在运行: {}", sessionId);
                return false;
            }
            
            // 检查视频文件是否存在
            if (!Files.exists(Paths.get(videoSource))) {
                log.error("视频文件不存在: {}", videoSource);
                session.setErrorMessage("视频文件不存在: " + videoSource);
                session.setStatus(SlamSession.SessionStatus.ERROR);
                slamSessionMapper.update(session);
                return false;
            }
            
            // 准备SLAM启动命令
            String slamScript = "/opt/smart-care-car/slam/start_slam.sh";
            String outputDir = "/opt/smart-care-car/slam/output";
            String sessionName = "slam_session_" + sessionId;
            
            ProcessBuilder pb = new ProcessBuilder(
                "bash", slamScript, videoSource, outputDir, sessionName
            );
            
            // 设置工作目录
            pb.directory(new File("/opt/smart-care-car/slam"));
            
            // 启动进程
            Process process = pb.start();
            runningProcesses.put(sessionId, process);
            
            // 初始化进程数据
            SlamProcessData data = new SlamProcessData();
            data.status = "RUNNING";
            processData.put(sessionId, data);
            
            // 更新会话状态
            session.setStatus(SlamSession.SessionStatus.RUNNING);
            session.setVideoSource(videoSource);
            session.setErrorMessage(null);
            slamSessionMapper.update(session);
            
            // 启动监控线程
            startProcessMonitoring(sessionId, process);
            
            log.info("✅ SLAM进程已启动: sessionId={}, videoSource={}", sessionId, videoSource);
            return true;
            
        } catch (Exception e) {
            log.error("启动SLAM进程失败: sessionId={}", sessionId, e);
            return false;
        }
    }
    
    /**
     * 停止SLAM处理进程
     */
    public boolean stopSlamProcess(Long sessionId) {
        try {
            Process process = runningProcesses.get(sessionId);
            if (process != null) {
                process.destroy();
                runningProcesses.remove(sessionId);
                processData.remove(sessionId);
                
                // 更新会话状态
                SlamSession session = slamSessionMapper.selectById(sessionId);
                if (session != null) {
                    session.setStatus(SlamSession.SessionStatus.STOPPED);
                    slamSessionMapper.update(session);
                }
                
                log.info("✅ SLAM进程已停止: sessionId={}", sessionId);
                return true;
            } else {
                log.warn("SLAM进程未运行: sessionId={}", sessionId);
                return false;
            }
        } catch (Exception e) {
            log.error("停止SLAM进程失败: sessionId={}", sessionId, e);
            return false;
        }
    }
    
    /**
     * 获取SLAM进程数据
     */
    public SlamProcessData getProcessData(Long sessionId) {
        return processData.get(sessionId);
    }
    
    /**
     * 启动进程监控
     */
    private void startProcessMonitoring(Long sessionId, Process process) {
        // 监控进程输出
        scheduler.submit(() -> {
            try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream()))) {
                
                String line;
                while ((line = reader.readLine()) != null) {
                    log.debug("SLAM输出 [{}]: {}", sessionId, line);
                    parseSlamOutput(sessionId, line);
                }
            } catch (IOException e) {
                log.error("读取SLAM进程输出失败: sessionId={}", sessionId, e);
            }
        });
        
        // 监控进程错误输出
        scheduler.submit(() -> {
            try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getErrorStream()))) {
                
                String line;
                while ((line = reader.readLine()) != null) {
                    log.warn("SLAM错误 [{}]: {}", sessionId, line);
                }
            } catch (IOException e) {
                log.error("读取SLAM进程错误输出失败: sessionId={}", sessionId, e);
            }
        });
        
        // 定期更新数据库
        scheduler.scheduleAtFixedRate(() -> {
            updateSessionData(sessionId);
        }, 5, 5, TimeUnit.SECONDS);
    }
    
    /**
     * 解析SLAM输出
     */
    private void parseSlamOutput(Long sessionId, String output) {
        SlamProcessData data = processData.get(sessionId);
        if (data == null) return;
        
        // 解析关键帧信息
        if (output.contains("KeyFrame")) {
            try {
                // 简单的正则匹配，实际可能需要更复杂的解析
                if (output.contains("Frame")) {
                    data.currentFrame++;
                    data.keyframes++;
                }
            } catch (Exception e) {
                log.debug("解析关键帧信息失败: {}", output);
            }
        }
        
        // 解析地图点信息
        if (output.contains("MapPoint")) {
            try {
                data.mapPoints += 10; // 假设每帧产生10个地图点
            } catch (Exception e) {
                log.debug("解析地图点信息失败: {}", output);
            }
        }
        
        // 更新轨迹长度（基于关键帧数量）
        data.trajectoryLength = data.keyframes * 0.1;
        
        // 更新进度
        if (data.totalFrames > 0) {
            data.progress = (double) data.currentFrame / data.totalFrames * 100.0;
        } else {
            data.progress = Math.min(data.currentFrame * 0.1, 100.0);
        }
        
        data.lastUpdate = LocalDateTime.now();
    }
    
    /**
     * 更新会话数据到数据库
     */
    private void updateSessionData(Long sessionId) {
        try {
            SlamProcessData data = processData.get(sessionId);
            if (data == null) return;
            
            SlamSession session = slamSessionMapper.selectById(sessionId);
            if (session == null) return;
            
            // 更新会话数据
            session.setCurrentFrame(data.currentFrame);
            session.setTotalFrames(data.totalFrames);
            session.setProgressPercentage(data.progress);
            
            // 检查进程是否还在运行
            Process process = runningProcesses.get(sessionId);
            if (process != null && !process.isAlive()) {
                session.setStatus(SlamSession.SessionStatus.STOPPED);
                runningProcesses.remove(sessionId);
                processData.remove(sessionId);
            }
            
            slamSessionMapper.update(session);
            
        } catch (Exception e) {
            log.error("更新会话数据失败: sessionId={}", sessionId, e);
        }
    }
    
    /**
     * 获取所有运行中的进程数据
     */
    public Map<Long, SlamProcessData> getAllProcessData() {
        return new HashMap<>(processData);
    }
    
    /**
     * 清理资源
     */
    public void shutdown() {
        // 停止所有进程
        runningProcesses.forEach((sessionId, process) -> {
            process.destroy();
        });
        runningProcesses.clear();
        processData.clear();
        
        // 关闭调度器
        scheduler.shutdown();
    }
}
